#include "yingce.h"
#include "actionmanager.h"
#include "globalsettings.h"
#include "libs/base/service/downloader.h"
#include "libs/base/utils/checklatestversion.h"
#include "libs/base/utils/packs.h"
#include "libs/base/utils/utils.h"
#include "libs/controls/autoupdatedialog.h"
#include "libs/controls/toastlabel.h"
#include "libs/controls/upgradedialog.h"
#include "libs/core/model/model.h"
#include "libs/core/user/localuser.h"
#include "services/yingceservice.h"
#include "user/changepassworddialog.h"
#include "user/editprofiledialog.h"
#include "user/logindialog.h"
#include "usersettings.h"
#include "utils/keeptokenlive.h"
#include "utils/utils.h"
#include "widgets/web/bridgeobjectmanager.h"
#include "yingcewindow.h"
#include <QApplication>
#include <QScreen>
#include <QTimer>

YingCe* YingCe::instance_ = nullptr;

YingCe::YingCe(QObject* parent) : QObject { parent }
{
    instance_ = this;

    bridge_object_manager_ = new BridgeObjectManager(QString::fromLatin1("YCApp"));

    main_task_queue_ = new yc::TaskGroup(1, false, 0, this);
    main_task_queue_->setKeepRunning(true);
    auth_task_queue_ = new yc::TaskGroup(100, false, 0, this);
    auth_task_queue_->setKeepRunning(true);
    main_task_queue_->addTask(auth_task_queue_);

    local_user_ = new yc::LocalUser(this);
    model_ = new yc::Model(this);

    const auto res_dir = UserSettings::instance().resDir();
    downloader_ = new yc::Downloader(YingCeService::instance(), QString(), res_dir, false, 0, this);
    downloader_->start();

    new ActionManager(this);

    keep_token_live_ = new KeepTokenLive(this);
    connect(keep_token_live_, &KeepTokenLive::tokenExpired, this, [this] {
        local_user_->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);
        UserSettings::instance().setLoginUser(
                yc::LoginUser { UserSettings::instance().loginUser().name(),
                                UserSettings::instance().loginUser().autoLogin() });
        login();
    });

    connect(YingCeService::instance(), &YingCeService::authenticationRequired, this,
            &YingCe::login);

    // 自动更新检测定时器
    QTimer* upgrade_timer = new QTimer(this);
    upgrade_timer->setInterval(7200000);
    connect(upgrade_timer, &QTimer::timeout, this, &YingCe::checkLatestVersion);
    upgrade_timer->start();
}

YingCe::~YingCe()
{
    if (window_) {
        delete window_;
        window_ = nullptr;
    }
    instance_ = nullptr;
}

YingCe* YingCe::instance()
{
    return instance_;
}

void YingCe::showMainWindow()
{
    if (!window_) {
        window_ = new YingCeWindow(nullptr);
        if (UserSettings::instance().windowState() == Qt::WindowMaximized) {
            window_->showMaximized();
        } else {
            // 设置窗口大小和位置
            const QRect& wr = UserSettings::instance().windowRect();
            if (wr.width() >= 800 && wr.height() >= 600) {
                QRect dr = QApplication::primaryScreen()->availableGeometry();
                if (wr.top() >= 0 && (wr.top() < (dr.bottom() - 80)) && (wr.left() > dr.left())
                    && (wr.left() < (dr.right() - 80))) {
                    // 左上角必须在桌面内
                    window_->setGeometry(QRect(100, 100, 1280, 720));
                }
            }
            window_->showNormal();
        }
    } else {
        window_->show();
        window_->activateWindow();
    }
}

bool YingCe::login()
{
    if (local_user_->offline()) {
        LoginDialog dlg(true, QApplication::activeWindow());
        dlg.exec();
    }
    return local_user_->online();
}

void YingCe::logout()
{
    if (local_user_->online()) {
        auto resolve = [this] {
            YingCeService::instance()->logout(yc::ResponseHandler(
                    this, [this](const yc::Request& request, const yc::Response& response) {
                        Q_UNUSED(request)
                        if (response.status() == 200) {
                            YingCeService::instance()->setAuthentication(QString());
                            local_user_->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);
                            local_user_->clearData();
                            UserSettings::instance().setLoginUser(yc::LoginUser {
                                    UserSettings::instance().loginUser().name(),
                                    UserSettings::instance().loginUser().autoLogin() });
                            if (window_) {
                                window_->logout();
                            }
                            model_->clear();
                        } else {
                            yc::ToastLabel::showMessage(tr("Failed to logout, the error is \"%1\".")
                                                                .arg(response.msg()),
                                                        QApplication::activeWindow());
                        }
                    }));
        };
        if (window_) {
            window_->requestStop(resolve, yc::RejectCallback {});
        }
    }
}

void YingCe::registerUser()
{
    if (local_user_->offline()) {
        if (window_) {
            window_->registerUser();
        }
    }
}

void YingCe::editUserProfile()
{
    EditProfileDialog dlg(QApplication::activeWindow());
    dlg.exec();
}

void YingCe::changePassword()
{
    ChangePasswordDialog dlg(QApplication::activeWindow());
    dlg.exec();
}

void YingCe::deleteUser()
{
    if (local_user_->online()) {
        auto ret = utils::msgBox(QApplication::activeWindow(), tr("Delete User"),
                                 tr("Are you sure delete this user, can't restore it?"),
                                 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
        if (ret == QMessageBox::Ok) {
            YingCeService::instance()->deleteUser(
                    local_user_->id(),
                    yc::ResponseHandler(
                            this, [this](const yc::Request& request, const yc::Response& response) {
                                Q_UNUSED(request)
                                if (response.status() == 0) {
                                    YingCeService::instance()->setAuthentication(QString());
                                    local_user_->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);
                                    UserSettings::instance().setLoginUser(yc::LoginUser {});
                                    if (window_) {
                                        window_->logout();
                                    }
                                } else {
                                    yc::ToastLabel::showMessage(
                                            tr("Failed to logout, the error is \"%1\".")
                                                    .arg(response.msg()),
                                            QApplication::activeWindow());
                                }
                            }));
        }
    }
}

void YingCe::requestStop(ResolveCallback resolve, RejectCallback reject)
{
    if (window_) {
        window_->close();
    }
}

void YingCe::quitApp(bool forced)
{
    if (forced) {
        mainTaskQueue()->terminateTasks();
        QCoreApplication::quit();
    } else {
        requestStop(ResolveCallback(&QCoreApplication::quit), RejectCallback());
    }
}

void YingCe::autoCheckUpgrade()
{
    Q_ASSERT(!UserSettings::instance().upgradeUrl().isEmpty());
    auto p = new yc::CheckLatestVersion(
            YingCeService::instance(), UserSettings::instance().upgradeUrl(),
            GlobalSettings::currentMajorVersion(), GlobalSettings::currentVersion(),
            yc::baseutils::macroStringToDateTime(GlobalSettings::buildDateTime()),
            UserSettings::instance().tempDir().absoluteFilePath("packs.xml"), 10000, this);
    connect(p, &yc::CheckLatestVersion::finished, this, &YingCe::onAutoCheckUpgradeFinished);
    connect(p, &yc::CheckLatestVersion::finished, p, &yc::CheckLatestVersion::deleteLater);
}

void YingCe::checkUpgrade()
{
    Q_ASSERT(!UserSettings::instance().upgradeUrl().isEmpty());
    if (!auto_update_dialog_ && !installing_pack_) {
        auto p = new yc::CheckLatestVersion(
                YingCeService::instance(), UserSettings::instance().upgradeUrl(),
                GlobalSettings::currentMajorVersion(), GlobalSettings::currentVersion(),
                yc::baseutils::macroStringToDateTime(GlobalSettings::buildDateTime()),
                UserSettings::instance().tempDir().absoluteFilePath("packs.xml"), 10000, this);
        connect(p, &yc::CheckLatestVersion::finished, this, &YingCe::onManualCheckUpgradeFinished);
        connect(p, &yc::CheckLatestVersion::finished, p, &yc::CheckLatestVersion::deleteLater);
    }
}

void YingCe::startAutoUpdate(const QString& latest_version)
{
    if (!auto_update_dialog_ && !installing_pack_) {
        yc::Packs packs(UserSettings::instance().tempDir().absoluteFilePath("packs.xml"));
        QList<yc::Pack> next_packs;
        if (latest_version.isEmpty()) {
            next_packs = packs.nextPacks(GlobalSettings::currentVersion());
        } else {
            yc::Pack pk = packs.pack(latest_version);
            if (!pk.version.isEmpty()) {
                next_packs.append(pk);
            }
        }
        if (!next_packs.empty()) {
            yc::AutoUpdateDialog* dlg = new yc::AutoUpdateDialog(QApplication::activeWindow());
            auto_update_dialog_ = dlg;
            dlg->setAttribute(Qt::WA_DeleteOnClose, true);
            dlg->resize(400, 176);
            connect(dlg, &yc::AutoUpdateDialog::beginToInstall, this,
                    [this] { installing_pack_ = true; });
            dlg->start(YingCeService::instance(), next_packs);
        }
    }
}

void YingCe::onAutoCheckUpgradeFinished(bool new_version, bool necessary,
                                        const QString& major_version, const QString& full_version,
                                        const QString& date)
{
    Q_UNUSED(major_version)
    Q_UNUSED(date)
    if (new_version) {
        if (necessary) {
            startAutoUpdate(full_version);
        } else {
            if (major_version != UserSettings::instance().ignoreMajorVersion()
                || date != UserSettings::instance().ignoreUpdateDate()) {
                showUpgradeDialog(true, major_version, full_version, date);
            }
        }
    }
}

void YingCe::onManualCheckUpgradeFinished(bool new_version, bool necessary,
                                          const QString& major_version, const QString& full_version,
                                          const QString& date)
{
    Q_UNUSED(necessary)
    Q_UNUSED(date)
    if (new_version && !necessary) {
        if (major_version == UserSettings::instance().ignoreMajorVersion()
            && date == UserSettings::instance().ignoreUpdateDate()) {
            new_version = false;
        }
    }
    yc::UpgradeDialog dlg(tr("YingCe"), GlobalSettings::currentMajorVersion(),
                          GlobalSettings::buildDateTime(), new_version, major_version, date,
                          QApplication::activeWindow());
    connect(&dlg, &yc::UpgradeDialog::upgradeRequested, this,
            [this, full_version] { startAutoUpdate(full_version); });
    connect(&dlg, &yc::UpgradeDialog::ignoreRequested, this, [major_version, date] {
        UserSettings::instance().setIgnoreMajorVersion(major_version);
        UserSettings::instance().setIgnoreUpdateDate(date);
    });
    dlg.resize(504, 226);
    dlg.exec();
}

void YingCe::checkLatestVersion()
{
    if (!auto_update_dialog_) {
        auto p = new yc::CheckLatestVersion(
                YingCeService::instance(), UserSettings::instance().upgradeUrl(),
                GlobalSettings::currentMajorVersion(), GlobalSettings::currentVersion(),
                yc::baseutils::macroStringToDateTime(GlobalSettings::buildDateTime()),
                UserSettings::instance().tempDir().absoluteFilePath("packs.xml"), 10000, this);
        connect(p, &yc::CheckLatestVersion::finished, this,
                [this](bool new_version, bool necessary, const QString& major_verison,
                       const QString& full_version, const QString& date) {
                    if (new_version) {
                        if (necessary) {
                            startAutoUpdate(full_version);
                        } else {
                            if (major_verison != UserSettings::instance().ignoreMajorVersion()
                                || date != UserSettings::instance().ignoreUpdateDate()) {
                                showUpgradeDialog(true, major_verison, full_version, date);
                            }
                        }
                    }
                });
        connect(p, &yc::CheckLatestVersion::finished, p, &yc::CheckLatestVersion::deleteLater);
    }
}

void YingCe::showUpgradeDialog(bool new_version, const QString& major_version,
                               const QString& full_version, const QString& date)
{
    yc::UpgradeDialog dlg(tr("YingCe"), GlobalSettings::currentVersion(),
                          GlobalSettings::buildDateTime(), new_version, major_version, date,
                          QApplication::activeWindow());
    connect(&dlg, &yc::UpgradeDialog::upgradeRequested, this,
            [this, full_version] { startAutoUpdate(full_version); });
    connect(&dlg, &yc::UpgradeDialog::ignoreRequested, this, [major_version, date] {
        UserSettings::instance().setIgnoreMajorVersion(major_version);
        UserSettings::instance().setIgnoreUpdateDate(date);
    });
    dlg.resize(504, 226);
    dlg.exec();
}
